import pandas as pd  # 导入pandas用于数据处理
import plotly.express as px  # 导入plotly express用于创建交互式图表
import plotly.graph_objects as go  # 导入plotly graph_objects用于创建自定义图表
from dash import Dash, html, dcc  # 导入Dash相关组件用于创建Web应用
from plotly.subplots import make_subplots  # 导入make_subplots用于创建子图

from DB_operations import BilibiliDB  # 导入自定义的数据库操作类

class BilibiliDashboard:
    """B站数据分析仪表板类
    这个类负责：
    1. 从数据库获取数据
    2. 处理和分析数据
    3. 创建可视化图表
    4. 构建Web界面展示分析结果
    """

    # 初始化仪表板
    def __init__(self):
        """
        初始化仪表板
        主要工作：
        1. 创建Dash应用实例
        2. 连接数据库并获取数据
        3. 数据预处理：
           - 转换时间格式
           - 计算视频时长
           - 计算互动指标
           - 处理分类数据
        4. 计算基础统计数据
        """
        # 初始化Dash应用
        self.app = Dash(__name__)


        # 连接数据库
        self.db = BilibiliDB()

        # 从数据库获取数据
        self.df = self.load_data()

        # 检查数据是否为空
        if self.df.empty:
            print("警告：数据库中没有数据，请先运行爬虫程序收集数据。")
            return

        # 转换时间格式便于后续时间分析
        self.df['publish_time'] = pd.to_datetime(self.df['publish_time'])
        self.df['crawl_time'] = pd.to_datetime(self.df['crawl_time'])

        # 将视频时长转换为秒数
        def convert_duration(duration):
            """
            将时长字符串（如'12:34'）转换为秒数
            支持两种格式：
            - MM:SS（分:秒）
            - HH:MM:SS（时:分:秒）
            """
            parts = duration.split(':')
            if len(parts) == 2:  # MM:SS格式
                return int(parts[0]) * 60 + int(parts[1])
            elif len(parts) == 3:  # HH:MM:SS格式
                return int(parts[0]) * 3600 + int(parts[1]) * 60 + int(parts[2])
            return 0

        self.df['duration_seconds'] = self.df['duration'].apply(convert_duration)

        # 计算各种互动率指标
        """
        互动指标说明：
        - 点赞率 = 点赞数/播放量
        - 投币率 = 投币数/播放量
        - 收藏率 = 收藏数/播放量
        - 总互动率 = (点赞数+投币数+收藏数)/播放量
        这些指标反映了视频的受欢迎程度和观众的参与度
        """
        self.df['like_rate'] = self.df['like_count'] / self.df['view_count']
        self.df['coin_rate'] = self.df['coin_count'] / self.df['view_count']
        self.df['favorite_rate'] = self.df['favorite_count'] / self.df['view_count']
        self.df['interaction_rate'] = (
                (self.df['like_count'] + self.df['coin_count'] + self.df['favorite_count'])
                / self.df['view_count']
        )

        # 处理分类数据，将空值填充为"其他"
        self.df['category'] = self.df['category'].fillna('其他')

        # 计算基础统计数据
        self.stats = {
            'total_videos': len(self.df),  # 视频总数
            'total_views': self.df['view_count'].sum(),  # 总播放量
            'avg_duration': self.df['duration_seconds'].mean() / 60,  # 平均时长（分钟）
            'avg_interaction_rate': self.df['interaction_rate'].mean(),  # 平均互动率
            'most_popular_category': self.df.groupby('category')['view_count'].mean().idxmax()  # 最受欢迎分区
        }

        # 提取发布时间的小时和星期��用于时间
        self.df['hour'] = self.df['publish_time'].dt.hour
        self.df['weekday'] = self.df['publish_time'].dt.day_name()

        # 计算UP主统计数据
        self.up_stats = self.df.groupby('up_name').agg({
            'view_count': 'mean',  # 平均播放量
            'like_count': 'mean',  # 平均点赞数
            'coin_count': 'mean',  # 平均投币数
            'favorite_count': 'mean',  # 平均收藏数
            'interaction_rate': 'mean'  # 平均互动率
        }).round(2)  # 保留2位小数

        # 设置页面布局
        self.setup_layout()

    # 加载数据
    def load_data(self):
        """
        从数据库加载数据
        返回:
            pd.DataFrame: 包含从数据库获取的数据的DataFrame
        """
        return self.db.get_videos_data()

    # 格式化数字
    def format_number(self, num):
        """
        将数字格式化为中文计数单位
        参数:
            num (float): 需要格式化的数字
        返回:
            str: 格式化后的字符串，如"1.2万"、"3.5亿"
        """
        if num >= 100000000:
            return f"{num / 100000000:.1f}亿"
        elif num >= 10000:
            return f"{num / 10000:.1f}万"
        return str(num)

    # 获取图表
    def get_chart_config(self):
        """
        获取图表通用配置
        返回:
            dict: 包含图表配置的字典，设置中文界面和工具栏选项
        """
        return {
            # 'displayModeBar': True,  # 显示工具栏
            # 'modeBarButtonsToAdd': [],  # 自定义添加的工具栏按钮
            # 'displaylogo': False,  # 不显示plotly logo
            'toImageButtonOptions': {  # 导出图片选项
                'filename': '图表导出',
                'format': 'png',
                'height': None,
                'width': None,
                'scale': 3
            },
            'scrollZoom': True,  # 允许滚轮缩放
            'showTips': True,  # 显示提示
            'showAxisDragHandles': True,  # 显示坐标轴拖动手柄
        }

    # 创建播放量TOP10视频图表
    def create_top_videos_chart(self):
        """
        创建播放量TOP10视频图表
        功能：
        - 展示播放量最高的10个视频
        - 使用水平条形图
        - 优化数值显示，避免重叠
        - 添加50万间隔的刻度线
        
        返回:
            dcc.Graph: 包含水平条形图的Dash图表组件
        """
        # 获取播放量前10的视频
        top_10 = self.df.nlargest(10, 'view_count')

        # 创建水平条形图
        fig = px.bar(
            top_10,
            x='view_count',
            y='title',
            orientation='h',
            title="播放量TOP10视频",
            labels={
                'view_count': '播放量',
                'title': '视频标题'
            }
        )

        # 计算刻度范围（每50万一个刻度）
        max_views = max(top_10['view_count'])
        step = 500000  # 50万
        ticks = list(range(0, int(max_views) + step, step))

        # 更新布局
        fig.update_layout(
            height=500,
            font=dict(size=14),
            # 设置标题显示
            yaxis={
                'categoryorder': 'total ascending',  # 按播放量排序
                'tickangle': 0,  # 标题水平显示
                'side': 'left'  # 标题在左侧显示
            },
            # 设置播放量显示
            xaxis=dict(
                side='top',  # 将x轴刻度显示在顶部
                showgrid=True,  # 显示网格线
                gridwidth=1,  # 网格线宽度
                gridcolor='lightgray',  # 网格线颜色
                tickformat="",  # 清除默认格式
                # 使用自定义刻度
                tickmode='array',
                tickvals=ticks,  # 设置刻度位置
                ticktext=[self.format_number(x) for x in ticks],  # 格式化刻度标签
                tickangle=0,  # 刻度标签水平显示
                showticklabels=True,  # 显示刻度标签
                title='播放量'  # 添加x轴标题
            ),
            margin=dict(l=20, r=20, t=60, b=20),  # 调整边距
            showlegend=False  # 隐藏图例
        )

        # 在条形图上直接显示播放量数值
        fig.update_traces(
            texttemplate='%{x:,.0f}',  # 显示完整数值
            textposition='outside',  # 数值显示在条形图外部
            textfont=dict(size=12),  # 设置字体大小
            textangle=0,  # 保持文字水平
            customdata=[self.format_number(x) for x in fig.data[0].x],  # 用于悬停显示
            hovertemplate="<b>%{y}</b><br>播放量: %{customdata}<extra></extra>"  # 悬停提示格式
        )

        return dcc.Graph(
            figure=fig,
            config=self.get_chart_config()
        )

    # 创建视频分区分布饼图
    def create_category_pie(self):
        """
        创建视频分区分布饼图
        功能：
        - 统计各个分区的视频数量
        - 使用饼图展示各分区占比
        - 添加交互式悬停提示
        
        ���回：
            dcc.Graph: 包含饼图的Dash图表组件，展示各分区视频数量占比
        """
        # 计算每个分区的视频数量
        category_counts = self.df['category'].value_counts()

        # 创建饼图
        fig = px.pie(
            values=category_counts.values,  # 各分区的视频数量
            names=category_counts.index,  # 分区名称
            title="视频分区分布"
        )

        # 更新悬停提示格式，显示分区名称和视频数量
        fig.update_traces(
            hovertemplate="分区: %{label}<br>数量: %{value}<br><extra></extra>"
        )

        return dcc.Graph(
            figure=fig,
            config=self.get_chart_config()
        )

    # 创建互动指标概览图表
    def create_engagement_bar(self):
        """
        创建互动指标概览图表
        功能：
        - 展示视频的平均互动数据
        - 包括播放量、点赞数、投币数、收藏数
        - 使用柱状图直观展示各指标的对比
        - 在柱状图顶部显示具体数值
        - Y轴每25万显示一个刻度
        
        返回：
            dcc.Graph: 包含柱状图的Dash图表组件，展示平均互动数据
        """
        # 定义要展示的指标及其中文名称
        metrics_dict = {
            'view_count': '播放量',
            'like_count': '点赞数',
            'coin_count': '投币数',
            'favorite_count': '收藏数'
        }

        # 准备数据
        metrics = ['view_count', 'like_count', 'coin_count', 'favorite_count']
        avg_metrics = self.df[metrics].mean()  # 计算平均值

        # 创建柱状图
        fig = px.bar(
            x=[metrics_dict[m] for m in metrics],  # X轴：指标名称
            y=avg_metrics.values,  # Y轴：平均值
            title="平均互动指标"
        )

        # 计算合适的刻度间隔（改为每25万一个刻度）
        max_value = max(avg_metrics.values)
        step = 250000  # 25万
        ticks = list(range(0, int(max_value) + step, step))

        # 更新图表布局
        fig.update_layout(
            height=500,
            font=dict(size=14),
            xaxis_title="互动类型",
            yaxis_title="平均数值",
            yaxis=dict(
                tickmode='array',
                tickvals=ticks,
                ticktext=[self.format_number(y) for y in ticks],  # 格式化刻度值
                range=[0, max_value * 1.1],  # 扩展Y轴范围，为数值显示留出空间
                showgrid=True,  # 显示网格线
                gridwidth=1,  # 网格线宽度
                gridcolor='lightgray'  # 网格线颜色
            ),
            showlegend=False,  # 隐藏图例
            margin=dict(l=20, r=20, t=60, b=20)  # 调整边距
        )

        # 更新柱状图显示
        fig.update_traces(
            text=[self.format_number(y) for y in avg_metrics.values],  # 显示格式化后的数值
            textposition='outside',  # 数值显示在柱状图顶部
            textfont=dict(
                size=12,  # 字体大小
                color='black'  # 字体颜色
            ),
            hovertemplate=(
                    "%{x}<br>" +
                    "数值: %{text}<br>" +
                    "<extra></extra>"
            ),
            cliponaxis=False  # 允许文本超出轴范围
        )

        return dcc.Graph(
            figure=fig,
            config=self.get_chart_config()
        )

    # 创建发布时间分布图表
    def create_hourly_line(self):
        """
        创建发布时间分布图表
        功能：
        - 统计24小时内视频发布时间的分布
        - 使用折线图展示发布时间趋势
        - X轴显示具体小时（0-23点）
        - Y轴显示视频数量
        
        返回：
            dcc.Graph: 包含折线图的Dash图表组件，展示��天中各时段的视频发布数量
        """
        # 统计每个小时的视频数量
        hour_counts = self.df['publish_time'].dt.hour.value_counts().sort_index()

        # 创建折线图
        fig = px.line(
            x=hour_counts.index,  # X轴：小时（0-23）
            y=hour_counts.values,  # Y轴：视频数量
            title="视频发布时间分布",
            labels={
                'x': '发布时间（小时）',
                'y': '视频数量'
            }
        )

        # 更新X轴刻度，显示为"X点"的格式
        fig.update_layout(
            xaxis=dict(
                ticktext=[f"{i}点" for i in range(24)],
                tickvals=list(range(24))
            ),
            font=dict(size=14)
        )

        return dcc.Graph(
            figure=fig,
            config=self.get_chart_config()
        )

    # 创建互动率TOP10视频图表
    def create_interaction_rate_chart(self):
        """
        创建互动率TOP10视频图表
        功能：
        - 计算并展示互动率最高的10个视频
        - 互动率 = (点赞数 + 投币数 + 收藏数) / 播放量
        - 使用水平条形图展示
        - 缩略显示视频标题
        - 在条形图右侧显示互动率数值
        
        返回：
            dcc.Graph: 包含水平条形图的Dash图表组件
        """
        # 选择互动率最高的10个视频
        top_interaction = self.df.nlargest(10, 'interaction_rate')

        # 处理视频标题：如果超过15个字符，截取前15个字符并加上省略号
        def truncate_title(title):
            return title[:15] + '...' if len(title) > 15 else title

        # 创建带有完整标题的自定义数据
        top_interaction['short_title'] = top_interaction['title'].apply(truncate_title)

        # 创建水平条形图
        fig = px.bar(
            top_interaction,
            x='interaction_rate',  # X轴：互动率
            y='short_title',  # Y轴：缩略后的视频标题
            orientation='h',  # 水平方向
            title="互动率TOP10视频",
            labels={
                'interaction_rate': '互动率',
                'short_title': '视频标题'
            }
        )

        # 更新布局
        fig.update_layout(
            height=500,
            font=dict(size=14),
            yaxis={
                'categoryorder': 'total ascending',  # 按互动率排序
                'tickangle': 0,  # 标题水平显示
                'side': 'left',  # 标题在左侧显示
            },
            xaxis=dict(
                title='互动率',
                tickformat='.1%',  # 显示为百分比格式
                showgrid=True,  # 显示网格线
                gridwidth=1,  # 网格线宽度
                gridcolor='lightgray',  # 网格线颜色
                range=[0, max(top_interaction['interaction_rate']) * 1.1]  # 扩展X轴范围，为数值显示留出空间
            ),
            margin=dict(l=20, r=50, t=60, b=20),  # 调整边距，右侧留更多空间
            hovermode='y',
            showlegend=False  # 隐藏图例
        )

        # 更新悬停提示和条形图上的数值显示
        fig.update_traces(
            customdata=top_interaction[['title', 'up_name']],  # 添加完整标题和UP主信息
            hovertemplate=(
                    "<b>%{customdata[0]}</b><br>" +  # 显示完整标题
                    "UP主: %{customdata[1]}<br>" +  # 显示UP主
                    "互动率: %{x:.1%}<br>" +  # 显示互动率
                    "<extra></extra>"
            ),
            texttemplate='%{x:.1%}',  # 显示互动率百分比
            textposition='outside',  # 数值显示在条形外部
            textfont=dict(
                size=12,  # 字体大小
                color='black'  # 字体颜色
            ),
            textangle=0,  # 保持文字水平
            cliponaxis=False  # 允许文本超出轴范围
        )

        return dcc.Graph(
            figure=fig,
            config=self.get_chart_config()
        )

    # 创建分区平均播放量图表
    def create_category_views_chart(self):
        """创建分区平均播放量图表"""
        category_views = self.df.groupby('category')['view_count'].mean()
        fig = px.bar(
            x=category_views.index,
            y=category_views.values,
            title="分区平均播放量"
        )

        # 计算合适的刻度间隔（改为50万）
        max_value = max(category_views.values)
        step = 500000  # 50万
        ticks = list(range(0, int(max_value) + step, step))

        fig.update_layout(
            xaxis_title="分区",
            yaxis_title="平均播放量",
            font=dict(size=14),
            height=500,  # 增加图表高度
            margin=dict(b=100),  # 增加底部边距，为倾斜的签留出空间
            xaxis=dict(
                tickangle=-45,  # 将x轴标签倾斜45度
                tickmode='array',
                ticktext=category_views.index,
                tickvals=list(range(len(category_views))),
            ),
            yaxis=dict(
                tickmode='array',
                tickvals=ticks,
                ticktext=[self.format_number(y) for y in ticks]
            ),
            bargap=0.3  # 调整柱子之间的间距
        )

        # 更新悬停提示格式
        fig.update_traces(
            customdata=[self.format_number(y) for y in category_views.values],
            hovertemplate="%{x}: %{customdata}<br><extra></extra>"
        )

        return dcc.Graph(
            figure=fig,
            config=self.get_chart_config()
        )

    # 创建UP主表现图表
    def create_up_performance_chart(self):
        """
        创建UP主表现图表
        功能：
        - 展示播放量TOP10的UP主的综��数据
        - 使用复合图表：
          * 柱状图展示平均播放量
          * 折线图展示平均点赞数和弹幕数
        - 使用双Y轴展示不同量级的数据
        - 添加格式化的数值显示
        
        返回：
            dcc.Graph: 包含复合图表的Dash图表组件
        """
        # 计算UP主的平均数据并选择TOP10
        up_stats = (
            self.df.groupby('up_name')
            .agg({
                'view_count': 'mean',
                'like_count': 'mean',
                'danmaku_count': 'mean'
            })
            .nlargest(10, 'view_count')  # 选择播放量最高的10个UP主
        )

        # 创建带有双Y轴的子图
        fig = make_subplots(specs=[[{"secondary_y": True}]])

        # 添加播放量柱状图（主Y轴）
        fig.add_trace(
            go.Bar(
                name='平均播放量',
                x=up_stats.index,
                y=up_stats['view_count'],
                text=[self.format_number(v) for v in up_stats['view_count']],
                textposition='auto',
                hovertemplate=(
                        "UP主: %{x}<br>" +
                        "平均播放量: %{text}<br>" +
                        "<extra></extra>"
                )
            ),
            secondary_y=False
        )

        # 添加点赞数和弹幕数折线图（次Y轴）
        for col in ['like_count', 'danmaku_count']:
            name = '平均点赞数' if col == 'like_count' else '平均弹幕数'
            fig.add_trace(
                go.Scatter(
                    name=name,
                    x=up_stats.index,
                    y=up_stats[col],
                    text=[self.format_number(v) for v in up_stats[col]],
                    mode='lines+markers+text',
                    textposition='top center',
                    hovertemplate=(
                            "UP主: %{x}<br>" +
                            f"{name}: " + "%{text}<br>" +
                            "<extra></extra>"
                    )
                ),
                secondary_y=True
            )

        # 计算主Y轴（播放量）的刻度
        max_views = max(up_stats['view_count'])
        view_step = 500000  # 50万
        view_ticks = list(range(0, int(max_views) + view_step, view_step))

        # 计算次Y轴（点赞和弹幕）的刻度
        max_secondary = max(
            max(up_stats['like_count']),
            max(up_stats['danmaku_count'])
        )
        secondary_step = 10000  # 1万
        secondary_ticks = list(range(0, int(max_secondary) + secondary_step, secondary_step))

        # 更新布局
        fig.update_layout(
            title="TOP10 UP主综合表现",
            xaxis_title="UP主",
            font=dict(size=14),
            legend_title="指标类型",
            showlegend=True,
            height=600,
            xaxis=dict(tickangle=-45)  # UP主名称倾斜显示
        )

        # 更新主Y轴（播放量）
        fig.update_yaxes(
            title_text="平均播放量",
            tickmode='array',
            tickvals=view_ticks,
            ticktext=[self.format_number(y) for y in view_ticks],
            secondary_y=False
        )

        # 更新次Y轴（点赞和弹幕）
        fig.update_yaxes(
            title_text="平均点赞/弹幕数",
            tickmode='array',
            tickvals=secondary_ticks,
            ticktext=[self.format_number(y) for y in secondary_ticks],
            secondary_y=True
        )

        return dcc.Graph(
            figure=fig,
            config=self.get_chart_config()
        )

    # 创建互动指标相关性散点图
    def create_correlation_scatter(self):
        """
        创建互动指标相关性散点图
        功能：
        - 展示播放量与投币数的关系
        - 使用气泡图：
          * X轴：播放量
          * Y轴：投币数
          * 气泡大小：互动率
          * 颜色：视频分区
        - 优化刻度显示，避免重叠
        
        返回：
            dcc.Graph: 包含气泡图的Dash图表组件
        """
        # 创建气泡图
        fig = px.scatter(
            self.df,
            x='view_count',  # X轴：播放量
            y='coin_count',  # Y轴：投币数
            size='interaction_rate',  # 气泡大小：互动率
            hover_data=['title', 'up_name', 'category'],  # 悬停显示的额外信息
            color='category',  # 按分区着色
            title="播放量与投币数关系",
            labels={
                'view_count': '播放量',
                'coin_count': '投币数',
                'title': '视频标题',
                'up_name': 'UP主',
                'category': '分区',
                'interaction_rate': '互动率'
            }
        )

        # 计算X轴和Y轴的刻度间隔
        max_x = max(self.df['view_count'])
        max_y = max(self.df['coin_count'])
        step_x = 500000  # 50万
        step_y = 25000  # 2.5万

        # 生成刻度值
        ticks_x = list(range(0, int(max_x) + step_x, step_x))
        ticks_y = list(range(0, int(max_y) + step_y, step_y))

        # 更新布局
        fig.update_layout(
            height=600,  # 增加图表高度
            font=dict(size=14),
            xaxis=dict(
                title='播放量',
                tickmode='array',
                tickvals=ticks_x,
                ticktext=[self.format_number(x) for x in ticks_x],
                tickangle=45,  # 将X轴刻度标签倾斜45度
                showgrid=True,
                gridwidth=1,
                gridcolor='lightgray'
            ),
            yaxis=dict(
                title='投币数',
                tickmode='array',
                tickvals=ticks_y,
                ticktext=[self.format_number(y) for y in ticks_y],
                showgrid=True,
                gridwidth=1,
                gridcolor='lightgray'
            ),
            # 调整图例位置到图表右侧
            legend=dict(
                yanchor="top",
                y=0.99,
                xanchor="left",
                x=1.02
            ),
            # 调整边距，为倾斜的刻度标签和图例留出空间
            margin=dict(l=20, r=150, t=60, b=80)
        )

        # 更新悬停提示
        fig.update_traces(
            hovertemplate=(
                    "<b>%{customdata[0]}</b><br>" +  # 视频标题
                    "UP主: %{customdata[1]}<br>" +  # UP主名称
                    "分区: %{customdata[2]}<br>" +  # 视频分区
                    "播放量: %{x:,.0f}<br>" +  # 播放量
                    "投币数: %{y:,.0f}<br>" +  # 投币数
                    "互动率: %{marker.size:.2%}<br>" +  # 互动���
                    "<extra></extra>"
            )
        )

        return dcc.Graph(
            figure=fig,
            config=self.get_chart_config()
        )

    # 创建响应式布局
    def setup_layout(self):
        """
        设置仪表板页面布局
        """
        # 添加CSS样式
        app_style = {
            'maxWidth': '1800px',  # 设置最大宽度
            'margin': 'auto',      # 居中显示
            'padding': '20px'      # 页面边距
        }

        chart_wrapper_style = {
            'width': '100%',       # 图表容器宽度
            'marginBottom': '30px' # 图表间距
        }

        flex_container_style = {
            'display': 'flex',
            'flexWrap': 'wrap',    # 允许换行
            'gap': '20px',         # 元素间距
            'marginBottom': '30px' # 容器底部间距
        }

        flex_item_style = {
            'flex': '1',
            'minWidth': '600px',   # 最小宽度
            'width': 'calc(50% - 10px)'  # 两列布局，考虑间距
        }

        self.app.layout = html.Div([
            # 页面标题
            html.H1("B站热门视频数据分析", style={'textAlign': 'center', 'marginBottom': '40px'}),

            # 第一行：全宽度的TOP10视频图表
            html.Div([
                html.Div([
                    html.H2("播放量TOP10视频"),
                    html.P(
                        "展示播放量最高的10个视频及其播放数据。数值越高表示视频越受欢迎。",
                        className="chart-description"
                    ),
                    self.create_top_videos_chart()
                ], style=chart_wrapper_style)
            ]),

            # 第二行：互动率TOP10和分区分布
            html.Div([
                html.Div([
                    html.H2("视频互动率TOP10"),
                    html.P(
                        "互动率 = (点赞数 + 投币数 + 收藏数) / 播放量",
                        className="chart-description"
                    ),
                    self.create_interaction_rate_chart()
                ], style=flex_item_style),

                html.Div([
                    html.H2("视频分区分布"),
                    html.P(
                        "展示不同分区的视频数量占比。",
                        className="chart-description"
                    ),
                    self.create_category_pie()
                ], style=flex_item_style)
            ], style=flex_container_style),

            # 第三行：互动数据概览和发布时间分布
            html.Div([
                html.Div([
                    html.H2("互动数据概览"),
                    html.P(
                        "展示平均播放量、点赞数等互动指标。",
                        className="chart-description"
                    ),
                    self.create_engagement_bar()
                ], style=flex_item_style),

                html.Div([
                    html.H2("发布时间分布"),
                    html.P(
                        "24小时内视频发布时间的分布情况。",
                        className="chart-description"
                    ),
                    self.create_hourly_line()
                ], style=flex_item_style)
            ], style=flex_container_style),

            # 第四行：相关性分析（全宽度）
            html.Div([
                html.Div([
                    html.H2("互动指标相关性"),
                    html.P("""播放量与投币数的相关性分析：""", className="chart-description"),
                    html.P("""• 右上角：高播放量+高投币数，表示视频整体表现好""", className="chart-description"),
                    html.P("""• 左上角：低播放量+高投币数，表示视频互动率高""", className="chart-description"),
                    html.P("""• 气泡大小表示互动率（投币数/播放量）""", className="chart-description"),
                    self.create_correlation_scatter()
                ], style=chart_wrapper_style)
            ])
        ], style=app_style)

    # 运行Dash应用
    def run(self):
        """
        运行Dash应用
        功能：
        - 启动Web服务器
        - 在默认浏览器中打开仪表板
        - 开启调试模式方便开发
        """
        self.app.run_server(debug=True)

if __name__ == '__main__':
    # 创建仪表板实例并运行
    dashboard = BilibiliDashboard()
    dashboard.run()
